#pragma once

// #define PBC_DEBUG

#include <gmp.h>
#include <gmpxx.h>
#include <assert.h>
#include <string>
#include <iostream>
#include "/usr/local/include/pbc/pbc.h"

struct pair_class;
struct ele_class;

struct pair_class {
    pairing_t pair;
    pair_class(const pair_class&) = delete;
    pair_class& operator=(const pair_class&) = delete;
    pair_class(const char* buf);
    pair_class(const char* buf, size_t len);
    pair_class(const std::string buf);
    ~pair_class();
    bool is_symmetric();
    void preprocess(ele_class ele_c);
    ele_class operator()(ele_class lhs, ele_class rhs);
    ele_class operator()(ele_class rhs);
private:
    pairing_pp_t pp;
    bool is_preprocess;
};
// pairing_pp_t pp; 加速运算

struct ele_class {
    element_t ele;
    ele_class();
    ele_class(pair_class& pair, const std::string& group);
    ele_class(const ele_class& ele_c);
    ele_class(element_t ele_t);
    ele_class& operator=(const ele_class& ele_c);
    ele_class& operator=(signed long int i);
    ele_class& operator=(mpz_class z);
    void set_str(const char* s, int base = 10);
    void set_str(std::string str, int base = 10);
    mpz_class to_mpz_class();
    ele_class operator+(const ele_class& ele_c);
    ele_class operator+(signed long int z);
    ele_class operator-(const ele_class& ele_c);
    ele_class operator*(const ele_class& ele_c);
    ele_class operator*(const mpz_class& z);
    ele_class operator*(signed long int z);
    ele_class operator/(const ele_class& ele_c);
    ele_class operator^(const ele_class& ele_c);  // 这里是做指数运算符
    ele_class operator^(const mpz_class& z);
    bool operator==(const ele_class& ele_c) const;
    bool operator==(signed long int z) const;
    bool operator!=(const ele_class& ele_c) const;
    bool operator!=(signed long int z) const;
    void random();  // 设置随机值
    ele_class inverse();
    void pow_preprocess();
    std::string to_string() const;
    ~ele_class();
private:
    element_pp_t ele_pp;
    bool is_pow_preprocess;
};

template <>
struct std::hash<ele_class> {
    size_t operator()(const ele_class &ele) const {
        return std::hash<string>()(ele.to_string());
    }
}; 

std::ostream & operator<<(std::ostream &os, const ele_class& ele_c);
